﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Windows;
using System.Windows.Threading;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Media3D;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Collections;


namespace FlyingCell
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private SmallAppMessage MySmallAppMsg;
        
        private Player MyPlayer;
        
        private Cell MyCell;
        private Line MyLine;
        private Grid MyGrid;
        private GridStack MyGridStack;

        UserControl3D MyVideoScreen;

        private BoundingBox MyGridStackBoundingBox;

        private enum ObjectSelect
        {
            CELL,
            LINE,
            GRID
        };
        ObjectSelect Selection;

        public MainWindow()
        {
            InitializeComponent();

            InputQueue = new Queue(250);
            
            MySmallAppMsg = new SmallAppMessage();
            SmallMsg.Children.Add(MySmallAppMsg);
            
            MyPlayer = new Player();
            MyPlayer.Open(new Uri(@"E:\Sony\FlyingCell\movie.wmv"));
            MyPlayer.Play();

            // Bounding box is used to specify where 3D object (ex: cell) must be considered visible or not
            MyGridStackBoundingBox = new BoundingBox(new Vector(0, 499), new Vector(-374, 250), new Vector(-400, 0));
            //MyGridStackBoundingBox = new BoundingBox(new Point3D(312, -62, -200), 312);

            MyVideoScreen = new UserControl3D("VideoScreen", (MeshGeometry3D)Resources["VideoScreenPanel"]);
            MyVideoScreen.AddTo3DScene(SceneViewport);
            MyVideoScreen.SetVisualContent(MyPlayer.GetBrush());
            
            MyGridStack = new GridStack();
            MyGridStack.AddTo3DSceneWithBounding(SceneViewport, MyGridStackBoundingBox);

            MyGridStack.StackGridCompleted += StackGrid_Completed;
            MyGridStack.UnstackGridCompleted += UnstackGrid_Completed;

            MyGridStack.StartStackGrid(null);
            MySmallAppMsg.Display("Please wait...");


            Selection = ObjectSelect.GRID;

            HandleInput();
        }

        private void Window_KeyDown(object sender, KeyEventArgs e)
        {
            Inputs TmpInput = new Inputs();

            switch (e.Key)
            {
                case Key.P:
                    MyPlayer.Play();
                    break;

                case Key.Left:
                    TmpInput = Inputs.SCROLL_LEFT;
                    break;

                case Key.Right:
                    TmpInput = Inputs.SCROLL_RIGHT;
                    break;

                case Key.NumPad4:
                    TmpInput = Inputs.LEFT;
                    break;

                case Key.NumPad6:
                    TmpInput = Inputs.RIGHT;
                    break;

                case Key.NumPad8:
                    TmpInput = Inputs.UP;
                    break;

                case Key.NumPad2:
                    TmpInput = Inputs.DOWN;
                    break;

                case Key.NumPad1:
                    TmpInput = Inputs.FORTH;
                    break;

                case Key.NumPad9:
                    TmpInput = Inputs.BACK;
                    break;

                case Key.PageUp:
                    TmpInput = Inputs.IN;
                    break;

                case Key.PageDown:
                    TmpInput = Inputs.OUT;
                    break;

                case Key.F1:
                    Selection = ObjectSelect.GRID;
                    break;

                case Key.F2:
                    Selection = ObjectSelect.LINE;
                    break;

                case Key.F3:
                    Selection = ObjectSelect.CELL;
                    break;

                case Key.F5:
                    if (MyCell != null)
                    {
                        MyCell.SetVisualContent(MyPlayer.GetBrush());
                    }
                    break;

                case Key.Escape:
                    TmpInput = Inputs.EXIT;
                    break;

                default:
                    break;

            }
            
            if (InputQueue != null)
            {
                InputQueue.Enqueue(TmpInput);
            }
        }


        //-------------------------------------------
        // INPUT MANAGEMENT
        //-------------------------------------------

        /// <summary>
        /// Enumerate all the possible input commands the application respond to.
        /// </summary>
        private enum Inputs
        {
            NONE,

            EXIT,
            UP,
            DOWN,
            LEFT,
            RIGHT,
            FORTH,
            BACK,
            IN,
            OUT,
            SCROLL_LEFT,
            SCROLL_RIGHT,
        }
        
        /// <summary>
        /// Queue of Inputs
        /// </summary>
        private Queue InputQueue;

        /// <summary>
        /// Input locker. Block input handler a certain time.
        /// </summary>
        bool IsInputLocked = false;

        /// <summary>
        /// Handle the input command
        /// </summary>
        public void HandleInput()
        {
            // TODO: hardcoded value!!!!
            int ScrollDuration = 100;

            if ((InputQueue.Count != 0) && (!IsInputLocked))
            {
                Inputs Input = (Inputs)InputQueue.Dequeue();
                int InputCount = 1;

                // check the repeatition of the input in the queue
                while (InputQueue.Count > 0)
                {
                    Inputs TmpInput = (Inputs)InputQueue.Peek();

                    if ((Input == TmpInput) && (IsInputRepeatMergeable(Input)))
                    {
                        InputQueue.Dequeue();
                        InputCount++;
                    }
                    else
                    {
                        break;
                    }
                }
                
                // Act on input
                switch (Input)
                {
                    case Inputs.SCROLL_LEFT:
                        LockInput();
                        MyLine.ScrollLeftCompleted += DefaultLine_Completed;
                        MyLine.StartScrollLeft(ScrollDuration/InputCount);
                        break;

                    case Inputs.SCROLL_RIGHT:
                        LockInput();
                        MyLine.ScrollRightCompleted += DefaultLine_Completed;
                        MyLine.StartScrollRight(ScrollDuration/InputCount);
                        break;

                    case Inputs.IN:
                        LockInput();
                        MyGridStack.StartStackGrid(null);
                        MySmallAppMsg.Display("Please wait...");
                        break;

                    case Inputs.OUT:
                        LockInput();
                        MyGridStack.StartUnstackGrid();
                        MySmallAppMsg.Display("Please wait...");
                        break;

                    case Inputs.LEFT:
                        //LockInput();
                        switch (Selection)
                        {
                            case ObjectSelect.GRID:
                                //MyGrid.XTranslationCompleted += DefaultGrid_Completed;
                                MyGrid.StartXTranslation(-125, 500);
                                break;

                            case ObjectSelect.LINE:
                                //MyLine.XTranslationCompleted += DefaultLine_Completed;
                                MyLine.StartXTranslation(-125, 500);
                                break;

                            case ObjectSelect.CELL:
                                //MyCell.XTranslationCompleted += DefaultCell_Completed;
                                MyCell.StartXTranslation(-125, 500);
                                break;
                        }
                        break;

                    case Inputs.RIGHT:
                        //LockInput();
                        switch (Selection)
                        {
                            case ObjectSelect.GRID:
                                //MyGrid.XTranslationCompleted += DefaultGrid_Completed;
                                MyGrid.StartXTranslation(125, 500);
                                break;

                            case ObjectSelect.LINE:
                                //MyLine.XTranslationCompleted += DefaultLine_Completed;
                                MyLine.StartXTranslation(125, 500);
                                break;

                            case ObjectSelect.CELL:
                                //MyCell.XTranslationCompleted += DefaultCell_Completed;
                                MyCell.StartXTranslation(125, 500);
                                break;
                        }
                        break;

                    case Inputs.DOWN:
                        //LockInput();
                        switch (Selection)
                        {
                            case ObjectSelect.GRID:
                                //MyGrid.YTranslationCompleted += DefaultGrid_Completed;
                                MyGrid.StartYTranslation(-125, 500);
                                break;

                            case ObjectSelect.LINE:
                                //MyLine.YTranslationCompleted += DefaultLine_Completed;
                                MyLine.StartYTranslation(-125, 500);
                                break;

                            case ObjectSelect.CELL:
                                //MyCell.YTranslationCompleted += DefaultCell_Completed;
                                MyCell.StartYTranslation(-125, 500);
                                break;
                        }
                        break;

                    case Inputs.UP:
                        //LockInput();
                        switch (Selection)
                        {
                            case ObjectSelect.GRID:
                                //MyGrid.YTranslationCompleted += DefaultGrid_Completed;
                                MyGrid.StartYTranslation(125, 500);
                                break;

                            case ObjectSelect.LINE:
                                //MyLine.YTranslationCompleted += DefaultLine_Completed;
                                MyLine.StartYTranslation(125, 500);
                                break;

                            case ObjectSelect.CELL:
                                //MyCell.YTranslationCompleted += DefaultCell_Completed;
                                MyCell.StartYTranslation(125, 500);
                                break;
                        }
                        break;

                    case Inputs.BACK:
                        //LockInput();
                        switch (Selection)
                        {
                            case ObjectSelect.GRID:
                                //MyGrid.YTranslationCompleted += DefaultGrid_Completed;
                                MyGrid.StartZTranslation(-200, 500);
                                break;

                            case ObjectSelect.LINE:
                                //MyLine.ZTranslationCompleted += DefaultLine_Completed;
                                MyLine.StartZTranslation(-200, 500);
                                break;

                            case ObjectSelect.CELL:
                                //MyCell.ZTranslationCompleted += DefaultCell_Completed;
                                MyCell.StartZTranslation(-200, 500);
                                break;
                        }
                        break;

                    case Inputs.FORTH:
                        //LockInput();
                        switch (Selection)
                        {
                            case ObjectSelect.GRID:
                                //MyGrid.YTranslationCompleted += DefaultGrid_Completed;
                                MyGrid.StartZTranslation(200, 500);
                                break;

                            case ObjectSelect.LINE:
                                //MyLine.ZTranslationCompleted += DefaultLine_Completed;
                                MyLine.StartZTranslation(200, 500);
                                break;

                            case ObjectSelect.CELL:
                                //MyCell.ZTranslationCompleted += DefaultCell_Completed;
                                MyCell.StartZTranslation(200, 500);
                                break;
                        }
                        break;

                    case Inputs.EXIT:
                        Close();
                        break;

                    case Inputs.NONE:
                        break;

                    default:
                        break;
                }
            }

            // restart this work
            this.Dispatcher.BeginInvoke((ThreadStart)delegate() { HandleInput(); }, DispatcherPriority.Input, null);
        }

        /// <summary>
        /// Check if the repeatition of the input can be merged into one single action, 
        /// instead of reacting to each single input in the queue.
        /// </summary>
        /// <param name="Input">The input to check</param>
        /// <returns>True if mergeable, else false</returns>
        private bool IsInputRepeatMergeable(Inputs Input)
        {
            if ((Input == Inputs.SCROLL_LEFT) || (Input == Inputs.SCROLL_RIGHT))
            {
                return true;
            }
            return false;
        }
        
        /// <summary>
        /// Lock input manager
        /// </summary>
        private void LockInput()
        {
            IsInputLocked = true;
        }

        /// <summary>
        /// Unlock input manager
        /// </summary>
        private void UnlockInput()
        {
            IsInputLocked = false;
        }

        //-------------------------------------------
        // EVENT HANDLERS
        //-------------------------------------------

        private void DefaultCell_Completed(object sender, UserControl3D.UserControl3DStatusReport e)
        {
            UnlockInput();
        }

        private void DefaultLine_Completed(object sender, UserControl3D.UserControl3DStatusReport e)
        {
            UnlockInput();
        }

        private void DefaultGrid_Completed(object sender, UserControl3D.UserControl3DStatusReport e)
        {
            UnlockInput();
        }

        private void StackGrid_Completed(object sender, GridStack.GridStackStatusReport e)
        {
            MySmallAppMsg.Hide();

            MyGrid = MyGridStack.GetGrid(-1);
            if (MyGrid != null)
            {
                MyLine = (Line) MyGrid.Get(-1);
                MyCell = (Cell) MyLine.Get(1);
            }

            UnlockInput();
        }

        private void UnstackGrid_Completed(object sender, GridStack.GridStackStatusReport e)
        {
            MySmallAppMsg.Hide();

            MyGrid = MyGridStack.GetGrid(-1);
            if (MyGrid != null)
            {
                MyLine = (Line)MyGrid.Get(-1);
                MyCell = (Cell)MyLine.Get(1);
            }

            UnlockInput();
        }

    }
}
